Android 11 您所在的位置:网站首页 flutter 横竖屏切换动画 Android 11

Android 11

2024-07-04 12:55| 来源: 网络整理| 查看: 265

本文以Activity.setRequestedOrientation为入口梳理下横竖屏切换的详细流程。     代码均是基于最新的11.0版本。 

第一篇主要讲了横竖屏切换时的准备操作: 更新方向,执行冻屏,截图显示以及计算更新基于新的方向的DisplayInfo和Configuration。

第二篇主要讲下如何将更新后的Configuration通知到树形的窗口结构以及应用中。

第三篇主要讲下解冻的流程以及横竖屏旋转动画的流程。

 

场景:假设此时手机处于横屏90度状态,当前Activity处于前台resumed状态,点击button设置竖屏, 逻辑如下:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

整个流程如下图所示:

下面来一步步进行梳理:

Step 1. Activity.setRequestedOrientation /** * Change the desired orientation of this activity. If the activity * is currently in the foreground or otherwise impacting the screen * orientation, the screen will immediately be changed (possibly causing * the activity to be restarted). Otherwise, this will be used the next * time the activity is visible. 注释已经解释得很详细了,就是改变activity的期望方向 * * @param requestedOrientation An orientation constant as used in * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}. */ public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) { if (mParent == null) { // 现在版本已经不使用ActivityParent,因此mParent一直为null try { ActivityTaskManager.getService().setRequestedOrientation( mToken, requestedOrientation); } catch (RemoteException e) { // Empty } } else { mParent.setRequestedOrientation(requestedOrientation); } }

先看下设置的参数:  AcitivityInfo.screenOrientation

/** * Information you can retrieve about a particular application * activity or receiver. This corresponds to information collected * from the AndroidManifest.xml's ;activity; and * ;receiver; tags. */ public class ActivityInfo extends ComponentInfo implements Parcelable { /** @hide */ @IntDef(prefix = { "SCREEN_ORIENTATION_" }, value = { SCREEN_ORIENTATION_UNSET, SCREEN_ORIENTATION_UNSPECIFIED, SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_PORTRAIT, SCREEN_ORIENTATION_USER, SCREEN_ORIENTATION_BEHIND, SCREEN_ORIENTATION_SENSOR, SCREEN_ORIENTATION_NOSENSOR, SCREEN_ORIENTATION_SENSOR_LANDSCAPE, SCREEN_ORIENTATION_SENSOR_PORTRAIT, SCREEN_ORIENTATION_REVERSE_LANDSCAPE, SCREEN_ORIENTATION_REVERSE_PORTRAIT, SCREEN_ORIENTATION_FULL_SENSOR, SCREEN_ORIENTATION_USER_LANDSCAPE, SCREEN_ORIENTATION_USER_PORTRAIT, SCREEN_ORIENTATION_FULL_USER, SCREEN_ORIENTATION_LOCKED }) @Retention(RetentionPolicy.SOURCE) public @interface ScreenOrientation {} }

一个int型变量,可以设置的参数上面都已经列出来,详细的解释可以到ActivityInfo.java文件中查看。然后就通过binder调用到系统服务端。

 

Step 2. ActivityTaskManagerService.setRequestedOrientation /** * System service for managing activities and their containers (task, stacks, displays,... ). * * {@hide} */ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {}

通过代码来看,系统服务端就是ActivityTaskManagerService

@Override public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (mGlobalLock) { // 整个过程是在锁里执行的 ActivityRecord r = ActivityRecord.isInStackLocked(token); // 通过token找到对应的ActivityRecord实例 if (r == null) { return; } final long origId = Binder.clearCallingIdentity(); try { r.setRequestedOrientation(requestedOrientation); } finally { Binder.restoreCallingIdentity(origId); } } }

 

Step 3. ActivityRecord.setRequestedOrientation /** * An entry in the history stack, representing an activity. 应用端的Acitivity在系统am侧是以ActivityRecord形式存在的。 */ final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {} void setRequestedOrientation(int requestedOrientation) { // mayFreezeScreenLocked()方法就是判断Activity所在进程是否处于可freeze的状态 // return hasProcess() && !app.isCrashing() && !app.isNotResponding(); setOrientation(requestedOrientation, mayFreezeScreenLocked()); // 通知相应的listener mAtmService.getTaskChangeNotificationController(). notifyActivityRequestedOrientationChanged(task.mTaskId, requestedOrientation); } private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) { final IBinder binder = (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null; // 通过代码来看, ActivityRecord,WindowToken都并未复写该方法, 因此直接看WindowContainer. setOrientation(requestedOrientation, binder, this); // Push the new configuration to the requested app in case where it's not pushed, e.g. when // the request is handled at task level with letterbox. if (!getMergedOverrideConfiguration().equals( mLastReportedConfiguration.getMergedConfiguration())) { ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } } Step 4. WindowContainer.setOrientation /** * Sets the specified orientation of this container. It percolates this change upward along the * hierarchy to let each level of the hierarchy a chance to respond to it. * * @param orientation the specified orientation. Needs to be one of {@link * android.content.pm.ActivityInfo.ScreenOrientation}. * @param freezeDisplayToken uses this token to freeze display if orientation change is not * done. Display will not be frozen if this is {@code null}, which * should only happen in tests. * @param requestingContainer the container which orientation request has changed. Mostly used * to ensure it gets correct configuration. */ void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken, @Nullable ConfigurationContainer requestingContainer) { if (mOrientation == orientation) { return; } mOrientation = orientation; // 更新ActivityRecord实例的方向值 final WindowContainer parent = getParent(); if (parent != null) { if (getConfiguration().orientation != getRequestedConfigurationOrientation()) { // Resolve the requested orientation. // 如果请求的方向与当前ActivityRecord方向不一致,以父节点的config更新当前Activity的config // 存在一种可能, Activity与当前的系统方向不一致,例如size compact mode,系统竖屏情况下,会强制 // 横屏unsizable activity缩放显示,但是此Activity仍是横屏方向,此时此Activity设置竖屏,就不会引起系统方向更新,只是更新Activity的config。 onConfigurationChanged(parent.getConfiguration()); } onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); } }

就像view是以树形结构管理的,其实系统端的窗口也是以树形结构管理。画下10.0和11.0的窗口结构(11.0改动稍微大些)如下图所示:

 

 

Step 5. WindowContainer.onDescendantOrientationChanged /** * Called when this container or one of its descendants changed its requested orientation, and * wants this container to handle it or pass it to its parent. * * @param freezeDisplayToken freeze this app window token if display needs to freeze * @param requestingContainer the container which orientation request has changed * @return {@code true} if handled; {@code false} otherwise. */ boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken, @Nullable ConfigurationContainer requestingContainer) { final WindowContainer parent = getParent(); if (parent == null) { return false; } return parent.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); }

沿着树结构往上,该函数仅有Task和DisplayContent复写。针对全屏情况而言,这里仅看DisplayContent的相关逻辑。

 

Step 6. DisplayContent.onDescendantOrientationChanged @Override boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, ConfigurationContainer requestingContainer) { // 1. 综合当前信息判断是否更新方向,执行冻屏,更新config,displayInfo等信息 关键函数,代码量较大: Step 7 -- Step 22 final Configuration config = updateOrientation( getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */); // If display rotation class tells us that it doesn't consider app requested orientation, // this display won't rotate just because of an app changes its requested orientation. Thus // it indicates that this display chooses not to handle this request. final boolean handled = getDisplayRotation().respectAppRequestedOrientation(); if (config == null) { return handled; } if (handled && requestingContainer instanceof ActivityRecord) { final ActivityRecord activityRecord = (ActivityRecord) requestingContainer; final boolean kept = updateDisplayOverrideConfigurationLocked(config, activityRecord, false /* deferResume */, null /* result */); activityRecord.frozenBeforeDestroy = true; if (!kept) { mRootWindowContainer.resumeFocusedStacksTopActivities(); } } else { // We have a new configuration to push so we need to update ATMS for now. // TODO: Clean up display configuration push betwe


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有